﻿using System;

#if WINDOWS_PHONE
namespace Fibre.WP7.Threading
#else
#if SILVERLIGHT
namespace Fibre.Silverlight.Threading
#else
namespace Fibre.Threading
#endif
#endif
{
    internal class WorkContext<TResult> : IWorkContext<TResult>
    {
        private readonly AsyncWork _asyncWork;
        private readonly WorkItemBuilder<TResult> _contextWorkItemBuilder;

        public WorkContext(AsyncWork asyncWork, Func<TResult> work)
        {
            _asyncWork = asyncWork;
            _contextWorkItemBuilder = new WorkItemBuilder<TResult>(work);
        }

        public IWorkContext AddWork(Action work)
        {
            return _asyncWork.AddWork(work);
        }

        public IWorkContext AddWork<TArg>(Func<TArg, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, Action<IAsyncResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, complete);
        }

        public IWorkContext<TWorkResult> AddWork<TWorkResult>(Func<TWorkResult> work)
        {
            return _asyncWork.AddWork(work);
        }

        public IWorkContext<TWorkResult> AddWork<TWorkResult>(Func<AsyncCallback, object, IAsyncResult> work, Func<IAsyncResult, TWorkResult> complete)
        {
            return _asyncWork.AddWork(work, complete);
        }

        public IWorkContext<TWorkResult> AddWork<TArg, TWorkResult>(Func<TArg, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, Func<IAsyncResult, TWorkResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, complete);
        }

        public IWorkContext AddWork<TArg, TArg2>(Func<TArg, TArg2, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, TArg2 asyncArgument2, Action<IAsyncResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, asyncArgument2, complete);
        }

        public IWorkContext<TWorkResult> AddWork<TArg, TArg2, TWorkResult>(Func<TArg, TArg2, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, TArg2 asyncArgument2, Func<IAsyncResult, TWorkResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, asyncArgument2, complete);
        }

        public IWorkContext AddWork(Func<AsyncCallback, object, IAsyncResult> work, Action<IAsyncResult> complete)
        {
            return _asyncWork.AddWork(work, complete);
        }

        public IWorkContext WhenComplete(Action<IWorkCompleteContext> completionContext)
        {
            throw new InvalidOperationException("You must call the generic overload of when completed");
        }

        public IWorkContext<TResult> WhenComplete(Action<IWorkCompleteContext<TResult>> completionContext)
        {
            _contextWorkItemBuilder.AddCompleteHandler(
                result =>
                    {
                        var workCompleteContext = new WorkCompleteContext<TResult>(_asyncWork)
                                                      {
                                                          Result = result.Result,
                                                          Error = result.Exception
                                                      };
                        completionContext(workCompleteContext);
                        _asyncWork.PerformWork();
                    });
            
            return this;
        }

        public AsyncWork WhenAllComplete(Action action)
        {
            return _asyncWork.WhenAllComplete(action);
        }

        public WorkItem Build()
        {
            return _contextWorkItemBuilder.BuildWorkItem();
        }

        public ICanWait PerformWork()
        {
            _asyncWork.PerformWork();
            return _asyncWork;
        }
    }

    internal class WorkContext : IWorkContext
    {
        private readonly AsyncWork _asyncWork;
        private readonly WorkItemBuilder _contextWorkItemBuilder;

        public WorkContext(AsyncWork asyncWork, Action work)
        {
            _asyncWork = asyncWork;
            _contextWorkItemBuilder = new WorkItemBuilder(work);
        }

        public IWorkContext AddWork(Action work)
        {
            return _asyncWork.AddWork(work);
        }

        public IWorkContext AddWork<TArg>(Func<TArg, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, Action<IAsyncResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, complete);
        }

        public IWorkContext<TResult> AddWork<TResult>(Func<TResult> work)
        {
            return _asyncWork.AddWork(work);
        }

        public IWorkContext<TResult> AddWork<TResult>(Func<AsyncCallback, object, IAsyncResult> work, Func<IAsyncResult, TResult> complete)
        {
            return _asyncWork.AddWork(work, complete);
        }

        public IWorkContext<TResult> AddWork<TArg, TResult>(Func<TArg, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, Func<IAsyncResult, TResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, complete);
        }

        public IWorkContext AddWork<TArg, TArg2>(Func<TArg, TArg2, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, TArg2 asyncArgument2, Action<IAsyncResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, asyncArgument2, complete);
        }

        public IWorkContext<TResult> AddWork<TArg, TArg2, TResult>(Func<TArg, TArg2, AsyncCallback, object, IAsyncResult> work, TArg asyncArgument, TArg2 asyncArgument2, Func<IAsyncResult, TResult> complete)
        {
            return _asyncWork.AddWork(work, asyncArgument, asyncArgument2, complete);
        }

        public IWorkContext AddWork(Func<AsyncCallback, object, IAsyncResult> work, Action<IAsyncResult> complete)
        {
            return _asyncWork.AddWork(work, complete);
        }

        public IWorkContext WhenComplete(Action<IWorkCompleteContext> completionContext)
        {
            _contextWorkItemBuilder.AddCompleteHandler(
                r =>
                {
                    var workCompleteContext = new WorkCompleteContext(_asyncWork)
                                                  {
                                                      Error = r.Exception
                                                  };
                    completionContext(workCompleteContext);
                    _asyncWork.PerformWork();
                });

            return this;
        }

        public AsyncWork WhenAllComplete(Action action)
        {
            return _asyncWork.WhenAllComplete(action);
        }

        public WorkItem Build()
        {
            return _contextWorkItemBuilder.BuildWorkItem();
        }

        public ICanWait PerformWork()
        {
            _asyncWork.PerformWork();
            return _asyncWork;
        }
    }
}